/*
 *Copyright (c) 2024 Black Sesame Technologies
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
*/

#include "route.h"
#include "printf_inc.h"
#include <string.h>
#include "can_output.h"
#include "eth2can.h"
#include "vcan.h"

/* ************CAN Interface RX Direction, Correspondence between Filter and Routing Table - START ***********/
/* CAN RX direction, structure definition. After receiving a CAN message,
 * query the hit filter to find the corresponding route table
 */
typedef enum {
    VCAN_FILTER_ROUTE_TABLE_MAP_TYPE_FAST_TABLE,
    VCAN_FILTER_ROUTE_TABLE_MAP_TYPE_COMMON_TABLE
} CAN_FILTER_ROUTE_TABLE_MAP_TYPE;

typedef struct {
    CAN_FILTER_ROUTE_TABLE_MAP_TYPE type;
    LLCG_RouteTableBaseType *fast_route_table;
    LLCG_CommonRouteTableType *common_route_table;
    uint16 common_route_element_count;
} CAN_FILTER_ROUTE_TABLE_MAP_BASE;

typedef struct {
    CAN_FILTER_ROUTE_TABLE_MAP_BASE map[LLCG_CONFIG_MAXFILTER_COUNT];
} CAN_FILTER_ROUTE_TABLE_MAP_BASE_CONTROLLER;

/* ************************************CAN接口RX方向，路由表变量   START************************/

/* ************************************CAN RX direction, route table variables - START************************/
// Fast route table is essentially a pointer to LLCG_RouteTableBaseType
// Shared by all controllers
static LLCG_RouteTableBaseType *local_base_route_table_ptr = (LLCG_RouteTableBaseType *)VCAN_BASE_ROUTE_TABLE_TCM_ADDR;
// Total length of the base route table
static uint32 local_base_route_table_size = 0;
// Number of entries in the base route table
static uint32 local_base_route_table_element_count = 0;

#define COMMON_ROUTE_TABLE_PTR(_controller_id)                                                                         \
    (LLCG_CommonRouteTableType *)(VCAN_COMMON_ROUTE_TABLE_TCM_ADDR +                                                   \
                                  (_controller_id) * VCAN_COMMON_ROUTE_TABLE_SIZE_EACH_CONTROLLER)

// Common route table storage area, each controller has its own common route table
// This is an array, each element of which is LLCG_CommonRouteTableType*
static LLCG_CommonRouteTableType *local_common_route_table_ptr[LLCG_CONFIG_MAXCTRL_COUNT] = {
    COMMON_ROUTE_TABLE_PTR(0),  COMMON_ROUTE_TABLE_PTR(1),  COMMON_ROUTE_TABLE_PTR(2),  COMMON_ROUTE_TABLE_PTR(3),
    COMMON_ROUTE_TABLE_PTR(4),  COMMON_ROUTE_TABLE_PTR(5),  COMMON_ROUTE_TABLE_PTR(6),  COMMON_ROUTE_TABLE_PTR(7),
    COMMON_ROUTE_TABLE_PTR(8),  COMMON_ROUTE_TABLE_PTR(9),  COMMON_ROUTE_TABLE_PTR(10), COMMON_ROUTE_TABLE_PTR(11),
    COMMON_ROUTE_TABLE_PTR(12), COMMON_ROUTE_TABLE_PTR(13), COMMON_ROUTE_TABLE_PTR(14), COMMON_ROUTE_TABLE_PTR(15)};
// Total length of the common route table
static uint32 local_common_route_table_size[LLCG_CONFIG_MAXCTRL_COUNT] = {0};
// Number of entries in the common route table
static uint32 local_common_route_table_element_count[LLCG_CONFIG_MAXCTRL_COUNT] = {0};
// Total route table
static CAN_FILTER_ROUTE_TABLE_MAP_BASE_CONTROLLER can_filter_route_table_map[LLCG_CONFIG_MAXCTRL_COUNT] = {0};
/* ************************************CAN RX direction, route table variables - END*************************/

/**
 * @brief Set the base route table
 *
 * @param route Base route table
 * @param route_n Number of entries in the base route table
 */
void can_base_route_table_set(LLCG_RouteTableBaseType *route, uint32 route_n)
{
    // Copy the route table to the TCM memory area
    memcpy(local_base_route_table_ptr, route, route_n * sizeof(LLCG_RouteTableBaseType));
    local_base_route_table_element_count = route_n;
}

/**
 * @brief Set the common route table for a specific controller
 *
 * @param controller_id Controller ID
 * @param route Common route table
 * @param route_n Number of entries in the common route table
 */
void can_common_route_table_set(uint32 controller_id, LLCG_CommonRouteTableType *route, uint32 route_n)
{
    memcpy(local_common_route_table_ptr[controller_id], route, route_n * sizeof(LLCG_RouteTableBaseType));
    local_common_route_table_size[controller_id] = route_n * sizeof(LLCG_RouteTableBaseType);
    local_common_route_table_element_count[controller_id] = route_n;
}

/**
 * @brief Set a fast route filter for a specific controller and filter
 *
 * @param controller_id Controller ID
 * @param filter_id Filter ID
 * @param route_index Index of the route in the base route table
 */
void can_filter_fast_route_set(uint32 controller_id, uint32 filter_id, uint32 route_index)
{
    can_filter_route_table_map[controller_id].map[filter_id].type = VCAN_FILTER_ROUTE_TABLE_MAP_TYPE_FAST_TABLE;
    can_filter_route_table_map[controller_id].map[filter_id].fast_route_table =
        &(local_base_route_table_ptr[route_index]);
    can_filter_route_table_map[controller_id].map[filter_id].common_route_table = NULL;

    // can2MB configuration
    LLCG_RouteTableBaseType *frt = can_filter_route_table_map[controller_id].map[filter_id].fast_route_table;
    if (frt->Destination & LLCG_ROUTETABLE_CAN_2_CAN) {
        int dest_ctrl_idx = 0;
        for (int i = 0; i < LLCG_CONFIG_MAXCTRL_COUNT; ++i) {
            if (frt->Can2CanOptions.u8DestHwChList[i] == 0)
                continue;

            dest_ctrl_idx = i;
            can_tx_mb_map_set(controller_id, filter_id, dest_ctrl_idx);
        }
    }
}

/**
 * @brief Set a common route filter for a specific controller and filter
 *
 * @param controller_id Controller ID
 * @param filter_id Filter ID
 */
void can_filter_common_route_set(uint32 controller_id, uint32 filter_id)
{
    can_filter_route_table_map[controller_id].map[filter_id].type = VCAN_FILTER_ROUTE_TABLE_MAP_TYPE_COMMON_TABLE;
    can_filter_route_table_map[controller_id].map[filter_id].fast_route_table = NULL;
    can_filter_route_table_map[controller_id].map[filter_id].common_route_table =
        local_common_route_table_ptr[controller_id];
    can_filter_route_table_map[controller_id].map[filter_id].common_route_element_count =
        local_common_route_table_element_count[controller_id];
}

/**
 * @brief Query the route rule by message ID in the route table
 *
 * @param msg_id Message ID
 * @param can_filter_rtables Pointer to CAN filter route tables
 * @return LLCG_RouteTableBaseType* Pointer to the route table
 */
static LLCG_RouteTableBaseType *can_route_rule_query_by_table(uint32 msg_id,
                                                              CAN_FILTER_ROUTE_TABLE_MAP_BASE *can_filter_rtables)
{
    LLCG_RouteTableBaseType *route_table = NULL;
    uint32 i;

    // hprintf(DEBUG, "%s: get_route_rule type:%d\n", __func__, can_filter_rtables->type);
    switch (can_filter_rtables->type) {
        case VCAN_FILTER_ROUTE_TABLE_MAP_TYPE_FAST_TABLE:
            route_table = can_filter_rtables->fast_route_table;
            break;

        case VCAN_FILTER_ROUTE_TABLE_MAP_TYPE_COMMON_TABLE:
            for (i = 0; i < can_filter_rtables->common_route_element_count; ++i) {
                if (msg_id == can_filter_rtables->common_route_table[i].u32CanID) {
                    route_table = can_filter_rtables->common_route_table[i].pRouteTable;
                    break;
                }
            }
            if (route_table == NULL)
                hprintf(ERROR, "*ERROR*: msg_id has not been configured in common route table. msg_id = %d \r\n",
                        msg_id);
            break;

        default:
            hprintf(ERROR, "*ERROR*: Queried msg type does not match any route type. msg_id = %d \r\n", msg_id);
            break;
    }

    return route_table;
}

/**
 * @brief Query the route table for a specific controller, hardware object, and message ID
 *
 * @param controller_id Controller ID
 * @param hwobj_id Hardware object ID
 * @param msg_id Message ID
 * @return LLCG_RouteTableBaseType* Pointer to the route table
 */
LLCG_RouteTableBaseType *can_route_query(uint8 controller_id, uint8 hwobj_id, uint32 msg_id)
{
    CAN_FILTER_ROUTE_TABLE_MAP_BASE *can_filter_rtables;

    can_filter_rtables = &(can_filter_route_table_map[controller_id].map[hwobj_id]);
    return can_route_rule_query_by_table(msg_id, can_filter_rtables);
}
